package com.cardone.common.util;

import com.google.common.base.Objects;
import com.google.common.collect.*;
import lombok.extern.slf4j.*;
import org.apache.commons.lang3.*;
import org.springframework.beans.*;

import java.beans.*;
import java.lang.reflect.*;
import java.util.*;

/**
 * bean 工具
 *
 * @author yaohaitao
 */
@Slf4j
public class BeanUtils {
    private BeanUtils() {
    }

    /**
     * 复制属性
     *
     * @param source            源对象
     * @param target            目标对象
     * @param includeProperties 包含属性
     * @throws BeansException
     */
    public static void copyProperties(final Object source, final Object target, final String... includeProperties) throws BeansException {
        final Class<?> actualEditable = target.getClass();

        final PropertyDescriptor[] targetPds = org.springframework.beans.BeanUtils.getPropertyDescriptors(actualEditable);

        for (final PropertyDescriptor targetPd : targetPds) {
            if ((targetPd.getWriteMethod() != null) && ((includeProperties == null) || (ArrayUtils.contains(includeProperties, targetPd.getName())))) {
                final PropertyDescriptor sourcePd = org.springframework.beans.BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());

                if ((sourcePd != null) && (sourcePd.getReadMethod() != null)) {
                    final Method readMethod = sourcePd.getReadMethod();

                    BeanUtils.methodSetAccessibleTrue(readMethod);

                    try {
                        final Object value = readMethod.invoke(source);

                        final Method writeMethod = targetPd.getWriteMethod();

                        BeanUtils.methodSetAccessibleTrue(writeMethod);

                        writeMethod.invoke(target, value);
                    } catch (final IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                        BeanUtils.log.error(e.getMessage(), e);
                    }
                }
            }
        }
    }

    private static void methodSetAccessibleTrue(final Method readMethod) {
        if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
            readMethod.setAccessible(true);
        }
    }

    /**
     * 比较两个对象,得到属性值不相等的属性名称集合
     *
     * @param newObject     新对象
     * @param oldObject     旧对象
     * @param useProperties 使用属性集合
     * @return 修改属性集合
     * @throws BeansException
     */
    public static String[] diffProperties(final Object newObject, final Object oldObject, final String... useProperties) throws BeansException {
        final Class<?> actualEditable = oldObject.getClass();

        final PropertyDescriptor[] oldPds = org.springframework.beans.BeanUtils.getPropertyDescriptors(actualEditable);

        final List<String> usePropertieList = Lists.newArrayList();

        for (final PropertyDescriptor oldPd : oldPds) {
            if ((oldPd.getReadMethod() == null) || !ArrayUtils.contains(useProperties, oldPd.getName())) {
                continue;
            }

            final PropertyDescriptor newPd = org.springframework.beans.BeanUtils.getPropertyDescriptor(newObject.getClass(), oldPd.getName());

            if ((newPd == null) || (newPd.getReadMethod() == null)) {
                continue;
            }

            final Method newReadMethod = newPd.getReadMethod();

            BeanUtils.methodSetAccessibleTrue(newReadMethod);

            try {
                final Object newValue = newReadMethod.invoke(newObject);

                final Method oldReadMethod = oldPd.getReadMethod();

                BeanUtils.methodSetAccessibleTrue(oldReadMethod);

                final Object oldValue = oldReadMethod.invoke(oldObject);

                if (!Objects.equal(newValue, oldValue)) {
                    usePropertieList.add(oldPd.getName());
                }
            } catch (final IllegalAccessException e) {
                BeanUtils.log.error(e.getMessage(), e);
            } catch (final IllegalArgumentException e) {
                BeanUtils.log.error(e.getMessage(), e);
            } catch (final InvocationTargetException e) {
                BeanUtils.log.error(e.getMessage(), e);
            }
        }

        return usePropertieList.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
    }
}